/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.repair.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.AllArgsConstructor;
import org.springblade.common.cache.DictCache;
import org.springblade.core.datascope.handler.ScopeModelHandler;
import org.springblade.core.mp.base.BaseEntity;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.modules.repair.dto.BuildingSelectSearchDTO;
import org.springblade.modules.repair.dto.MaintainTimeDTO;
import org.springblade.modules.repair.dto.MaintainTimeSubmitDTO;
import org.springblade.modules.repair.entity.*;
import org.springblade.modules.repair.mapper.BuildingMapper;
import org.springblade.modules.repair.service.*;
import org.springblade.modules.repair.vo.*;
import org.springblade.modules.repair.vo.plan.SignRangeVo;
import org.springblade.modules.repair.vo.rescue.MapRescueBuildingDetailVO;
import org.springblade.modules.repair.vo.statistics.*;
import org.springblade.modules.system.service.IDeptService;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 楼盘信息 服务实现类
 *
 * @author BladeX
 * @since 2021-05-21
 */
@Service
@AllArgsConstructor
public class BuildingServiceImpl extends BaseServiceImpl<BuildingMapper, Building> implements IBuildingService {

	private final ScopeModelHandler scopeModelHandler;
	private final BladeRedis bladeRedis;
	private final IDeptService deptService;
	private final IBuildingExtService buildingExtService;

	private final IBuildingMaintainTimeService buildingMaintainTimeService;

	private final RelationAliasService relationAliasService;

	@Lazy
	private final IBuildingFloorElevatorService buildingFloorElevatorService;
	@Lazy
	private final IElevatorService elevatorService;

	@Override
	public IPage<BuildingVO> selectBuildingPage(IPage<BuildingVO> page, BuildingVO building) {
		return page.setRecords(baseMapper.selectBuildingPage(page, building));
	}

	@Override
	public List<ListBuildingVO> listBuilding(String buildingName) {
		return baseMapper.listBuilding(buildingName);
	}

	@Override
	public IPage<BuildingSelectVO> myMaintainBuilding(BuildingSelectSearchDTO search, IPage<BuildingSelectVO> page) {
		if (Func.isEmpty(search.getUserIds())) {
			search.setUserIds(deptService.getUserIdByScope());
		}
		List<BuildingSelectVO> list = baseMapper.myMaintainBuildingList(page, search);
		return page.setRecords(list);
	}

	@Override
	public List<ElevatorSelectVO> myMaintainBuildingChild(BuildingSelectSearchDTO search) {
		if (Func.isEmpty(search.getUserIds())) {
			search.setUserIds(deptService.getUserIdByScope());
		}
		// 去重
		List<ElevatorSelectVO> list = baseMapper.myMaintainBuildingChild(search).stream().collect(
			Collectors.collectingAndThen(
				Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ElevatorSelectVO::getElevatorId))), ArrayList::new)
		);
		return list;
	}

	@Override
	public MyElevatorStatisticsVO myElevatorStatistics(BuildingSelectSearchDTO search) {
		if (Func.isEmpty(search.getUserIds())) {
			search.setUserIds(deptService.getUserIdByScope());
		}
		MyElevatorStatisticsVO statistics = new MyElevatorStatisticsVO();
		List<BuildingSelectVO> list = baseMapper.myElevatorStatistics(search);
		statistics.setBuildingTotal(list.stream().map(t -> t.getBuildingId()).distinct().count());
		statistics.setEleTotal(list.stream().map(t -> t.getElevatorId()).distinct().count());
		return statistics;
	}

	/**
	 * 所在机构及子级部门id
	 *
	 * @return
	 */
	private List<Long> getOwnDeptChild() {
		BladeUser bladeUser = AuthUtil.getUser();
		List<Long> deptIds = new ArrayList<>();
		if (!AuthUtil.isAdmin()) {
			List<Long> ownDeptIds = Func.toLongList(bladeUser.getDeptId());
			deptIds.addAll(ownDeptIds);
			ownDeptIds.forEach(deptId -> {
				List<Long> deptIdList = scopeModelHandler.getDeptAncestors(deptId);
				deptIds.addAll(deptIdList);
			});
		}
		return deptIds;
	}

	@Override
	public BuildingSubmitVO buildingSubmitVOR(Long buildingId) {
		// 2023-11-04 新增 车位、驻点、保养人数要求、预约要求, 即项目ext
		BuildingExt buildingExt = buildingExtService.getOne(new LambdaQueryWrapper<BuildingExt>()
			.eq(BuildingExt::getBuildingId, buildingId)
			.eq(BaseEntity::getIsDeleted, 0)
		);

		BuildingSubmitVO submitVO = baseMapper.buildingSubmitVOR(buildingId);
		if (!Func.isNull(buildingExt)) {
			String buildingImg = buildingExt.getBuildingImg();
			List<String> images = new ArrayList<>(10);
			if (StringUtil.isNotBlank(buildingImg) && !Func.isNull(buildingImg)) {
				String[] split = buildingImg.split(",");
				images.addAll(Arrays.asList(split));
			}
			submitVO.setBuildingImages(images);
		}

		if (ObjectUtil.isNotEmpty(submitVO) && ObjectUtil.isNotEmpty(submitVO.getType())) {
			submitVO.setTypeName(DictCache.getValue("building_type", submitVO.getType()));
		}
		// 2023-11-04 新增 项目别名返回
		List<RelationAlias> relationAliases = relationAliasService.list(new LambdaQueryWrapper<RelationAlias>()
			.eq(RelationAlias::getRelationId, buildingId)
			.eq(BaseEntity::getIsDeleted, 0)
		);
		buildingExt.setRelationAliases(relationAliases);
		submitVO.setBuildingExt(buildingExt);
		// 2023-11-07 新增 允许保养作业时间段
		List<MaintainTimeSubmitDTO> buildingMaintainTimes = buildingMaintainTimeService.selectMaintainTimes(buildingId);
		submitVO.setBuildingMaintainTimes(buildingMaintainTimes);
		// 2023-11-10 拆分buildingMaintainTimes,方便前端展示
		List<TimeListVO> timeList = new ArrayList<>(7);
		for (MaintainTimeSubmitDTO m : buildingMaintainTimes) {
			if (Func.isNotEmpty(m.getMaintainTimeList())) {
				List<MaintainTimeDTO> maintainTimeList = m.getMaintainTimeList();
				List<String> s = new ArrayList<>();
				for (MaintainTimeDTO maintainTimeDTO : maintainTimeList) {
					s.add(maintainTimeDTO.getStartTime() + "-" + maintainTimeDTO.getEndTime());
				}
				TimeListVO result = new TimeListVO();
				result.setLabel(String.valueOf(m.getWeek()));
				result.setWeekDay(m.getWeek());
				result.setTimeStamp(s);
				timeList.add(result);
			} else {
				TimeListVO result = new TimeListVO();
				result.setLabel(String.valueOf(m.getWeek()));
				result.setWeekDay(m.getWeek());
				timeList.add(result);
			}
		}
		submitVO.setTimeList(timeList);
		List<BuildingFloorElevator> elevatorList = buildingFloorElevatorService.list(Wrappers.<BuildingFloorElevator>lambdaQuery().eq(BuildingFloorElevator::getBuildingId, buildingId));
		submitVO.setFloorTotal(elevatorList.size());
		return submitVO;
	}


	@Override
	public PieChartVO comingExpiryProject(List<Long> userIds) {
		if (Func.isEmpty(userIds)) {
			userIds = deptService.getUserIdByScope();
		}
		return projectStatisticsDataHandle(3, userIds);
	}

	@Override
	public PieChartVO expiryProject(List<Long> userIds) {
		if (Func.isEmpty(userIds)) {
			userIds = deptService.getUserIdByScope();
		}
		return projectStatisticsDataHandle(1, userIds);
	}

	@Override
	public PieChartVO projectElevator(List<Long> userIds) {
		if (Func.isEmpty(userIds)) {
			userIds = deptService.getUserIdByScope();
		}
		return projectStatisticsDataHandle(null, userIds);
	}

	/**
	 * 项目统计数据封装
	 *
	 * @param condition 合同扩展状态(对应blade_building_contract_plus表contract_ext_status)
	 *                  1:待续签
	 *                  2:已续签
	 *                  3:即将到期
	 *                  4:已脱保
	 *                  5:已作废
	 * @return
	 */
	private PieChartVO projectStatisticsDataHandle(Integer condition, List<Long> userIds) {
		PieChartVO pieChartVO = new PieChartVO();
		List<BuildingStatisticsListVO> statisticsList = baseMapper.buildingStatisticsList(condition, userIds);
		pieChartVO.setValue1(statisticsList.stream().count());
		long eleTotal = statisticsList.stream().mapToLong(BuildingStatisticsListVO::getEleTotal).sum();
		pieChartVO.setValue2(eleTotal);

		List<ChartItemVO> items = new ArrayList<>();
		List<BuildingStatisticsListVO> top5List = statisticsList.stream().sorted(Comparator.comparing(BuildingStatisticsListVO::getEleTotal).reversed()).limit(4).collect(Collectors.toList());
		for (BuildingStatisticsListVO top : top5List) {
			ChartItemVO item = new ChartItemVO();
			item.setName(top.getBuildingName());
			item.setPercentage(NumberUtil.div(String.valueOf(top.getEleTotal()), String.valueOf(eleTotal), 3));
			item.setEleTotal(top.getEleTotal());
			items.add(item);
		}

		// ”其余“ 统计
		long top5EleTotal = top5List.stream().mapToLong(BuildingStatisticsListVO::getEleTotal).sum();
		if (top5EleTotal != eleTotal) {
			ChartItemVO item = new ChartItemVO();
			item.setName("其余");
			long otherEle = eleTotal - top5EleTotal;
			item.setEleTotal(otherEle);
			item.setPercentage(NumberUtil.div(String.valueOf(otherEle), String.valueOf(eleTotal), 3));
			items.add(item);
		}
		pieChartVO.setItems(items);
		return pieChartVO;
	}

	@Override
	public List<ChartItemVO> projectElevatorUseType(List<Long> userIds) {
		if (Func.isEmpty(userIds)) {
			userIds = deptService.getUserIdByScope();
		}
		List<ChartItemVO> chartList = new ArrayList<>();
		List<ElevatorStatisticsListVO> statisticsList = baseMapper.elevatorStatisticsList(userIds);
		if (CollectionUtils.isEmpty(statisticsList)) {
			return chartList;
		}
		long eleTotal = statisticsList.stream().count();
		Map<Integer, List<ElevatorStatisticsListVO>> map = statisticsList.stream().collect(Collectors.groupingBy(ElevatorStatisticsListVO::getUseType));
		map.forEach((key, value) -> {
			ChartItemVO item = new ChartItemVO();
			if (key == 0) {
				item.setName("年审梯");
			} else {
				item.setName("非年审梯");
			}
			List<ElevatorStatisticsListVO> itemList = value;
			long itemEleTotal = itemList.stream().count();
			item.setPercentage(NumberUtil.div(String.valueOf(itemEleTotal), String.valueOf(eleTotal), 3));
			item.setEleTotal(itemEleTotal);
			chartList.add(item);
		});
		return chartList;
	}

	@Override
	public PieChartVO brandElevator(List<Long> userIds) {
		if (Func.isEmpty(userIds)) {
			userIds = deptService.getUserIdByScope();
		}
		List<BrandStatisticsListVO> statisticsList = baseMapper.brandStatisticsList(userIds);
		;

		PieChartVO pieChartVO = new PieChartVO();
		pieChartVO.setValue1(statisticsList.stream().count());
		long eleTotal = statisticsList.stream().mapToLong(BrandStatisticsListVO::getEleTotal).sum();
		pieChartVO.setValue2(eleTotal);

		List<ChartItemVO> items = new ArrayList<>();
		List<BrandStatisticsListVO> top5List = statisticsList.stream().sorted(Comparator.comparing(BrandStatisticsListVO::getEleTotal).reversed()).limit(4).collect(Collectors.toList());
		for (BrandStatisticsListVO top : top5List) {
			ChartItemVO item = new ChartItemVO();
			item.setId(top.getBrandId());
			item.setName(top.getBrandName());
			item.setPercentage(NumberUtil.div(String.valueOf(top.getEleTotal()), String.valueOf(eleTotal), 3));
			item.setEleTotal(top.getEleTotal());
			items.add(item);
		}

		// ”其余“ 统计
		long top5EleTotal = top5List.stream().mapToLong(BrandStatisticsListVO::getEleTotal).sum();
		if (top5EleTotal != eleTotal) {
			ChartItemVO item = new ChartItemVO();
			item.setName("其余");
			long otherEle = eleTotal - top5EleTotal;
			item.setEleTotal(otherEle);
			item.setPercentage(NumberUtil.div(String.valueOf(otherEle), String.valueOf(eleTotal), 3));
			items.add(item);
		}
		pieChartVO.setItems(items);
		return pieChartVO;
	}

	@Override
	public List<SignRangeVo> getBuildingSignList() {
		List<BuildingSignVo> list = new ArrayList<>();
		List<SignRangeVo> resultList = new ArrayList<>();
		List<Building> searchList = this.list();
		for (Building building : searchList) {
			BuildingSignVo buildingSignVo = new BuildingSignVo();
			BeanUtil.copyProperties(building, buildingSignVo);
			list.add(buildingSignVo);
		}
		for (BuildingSignVo buildingSignVo : list) {
			SignRangeVo signRangeVo = new SignRangeVo();
			if (buildingSignVo.getLongitude() != null) {
				signRangeVo.setAddress(buildingSignVo.getName());
				signRangeVo.setLongitude(buildingSignVo.getLongitude().toString());
				signRangeVo.setLatitude(buildingSignVo.getLatitude().toString());
				resultList.add(signRangeVo);
			}
		}
		return resultList;
	}

	@Override
	public IPage<PrincipalBuildingListVO> personalBuildingList(BuildingSelectSearchDTO search, IPage<PrincipalBuildingListVO> page) {
		return page.setRecords(baseMapper.personalBuildingList(AuthUtil.getUserId(), search, page));
	}

	@Override
	public List<PrincipalElevatorListVO> personalElevatorList(BuildingSelectSearchDTO search) {
		List<PrincipalElevatorListVO> principalElevatorListVOS = baseMapper.personalElevatorList(AuthUtil.getUserId(), search);
		// 2024-10-23 新增根据所有完整度判断是否齐全
		principalElevatorListVOS.forEach(t -> {
			if(!Func.isNull(t.getElevatorId())){
				t.setIsComplete(isFull(t.getElevatorId()));
			}
		});
		return principalElevatorListVOS;
	}

	private boolean isOk(BigDecimal bigDecimal) {
		if (Func.isNull(bigDecimal) || bigDecimal.compareTo(new BigDecimal("1.00")) < 0) {
			return false;
		}
		return bigDecimal.compareTo(new BigDecimal("1.00")) == 0;
	}

	public boolean isFull(Long elevatorId){
		Elevator currentElevator = elevatorService.getById(elevatorId);
		BigDecimal jgIntact = currentElevator.getJgIntact();
		BigDecimal wbIntact = currentElevator.getWbIntact();
		BigDecimal bxIntact = currentElevator.getBxIntact();
		BigDecimal sbJsIntact = currentElevator.getSbJsIntact();
		BigDecimal sbZcIntact = currentElevator.getSbZcIntact();
		BigDecimal ccIntact = currentElevator.getCcIntact();
		BigDecimal syIntact = currentElevator.getSyIntact();
		BigDecimal azIntact = currentElevator.getAzIntact();
		BigDecimal twyIntact = currentElevator.getTwyIntact();
		return isOk(jgIntact)
			&& isOk(wbIntact)
			&& isOk(bxIntact)
			&& isOk(sbJsIntact)
			&& isOk(sbZcIntact)
			&& isOk(ccIntact)
			&& isOk(syIntact)
			&& isOk(azIntact)
			&& isOk(twyIntact);
	}

	@Override
	public IPage<PrincipalBuildingListVO> deptBuildingList(BuildingSelectSearchDTO search, IPage<PrincipalBuildingListVO> page) {
		if (Func.isNotEmpty(search.getUserIds())) {
			search.setPrincipalUsers(search.getUserIds().stream().map(t -> String.valueOf(t)).collect(Collectors.joining("|")));
		}
		return page.setRecords(baseMapper.deptBuildingList(search, page));
	}

	@Override
	public List<PrincipalElevatorListVO> deptElevatorList(BuildingSelectSearchDTO search) {
		if (Func.isNotEmpty(search.getUserIds())) {
			search.setPrincipalUsers(search.getUserIds().stream().map(t -> String.valueOf(t)).collect(Collectors.joining("|")));
		}
		List<PrincipalElevatorListVO> principalElevatorListVOS = baseMapper.deptElevatorList(search);
		if(Func.isNotEmpty(principalElevatorListVOS)){
			principalElevatorListVOS.forEach(t -> {
					if(!Func.isNull(t.getElevatorId())){
						t.setIsComplete(isFull(t.getElevatorId()));
					}}
			);
		}
		return principalElevatorListVOS;
	}

	@Override
	public MyElevatorStatisticsVO principalBuildingStatistics(BuildingSelectSearchDTO search) {
		List<PrincipalElevatorListVO> list = null;
		if (!Func.isEmpty(search.getModule()) && search.getModule() == 1) {
			list = this.deptElevatorList(search);
		} else {
			list = this.personalElevatorList(search);
		}

		MyElevatorStatisticsVO statistics = new MyElevatorStatisticsVO();
		statistics.setBuildingTotal(list.stream().map(t -> t.getBuildingId()).distinct().count());
		statistics.setEleTotal(list.stream().map(t -> t.getElevatorId()).distinct().count());

		return statistics;
	}

	@Override
	public IPage<BuildingSelectVO> getBuildingList(IPage<BuildingSelectVO> page, BuildingSelectSearchDTO search) {
		return baseMapper.getBuildingList(page, search);
	}

	@Override
	public List<BuildingRedisVO> listForVO() {
		return baseMapper.listForRedisVO();
	}

	@Override
	public BuildingExt selectBuildingExtDetail(Long buildingId) {
		BuildingExt buildingExt = buildingExtService.getOne(Wrappers.<BuildingExt>lambdaQuery().eq(BuildingExt::getBuildingId, buildingId));
		buildingExt.setBuilding(this.getById(buildingId));
		return buildingExt;
	}

	@Override
	public IPage<BuildingSelectVO> rescueBuildingList(BuildingSelectSearchDTO search, IPage<BuildingSelectVO> page) {
		return baseMapper.rescueBuildingList(page, search);
	}

	@Override
	public IPage<ElevatorSelectVO> rescueElevatorList(BuildingSelectSearchDTO search, IPage<ElevatorSelectVO> page) {
		return baseMapper.rescueElevatorList(page, search);
	}

	@Override
	public MapRescueBuildingDetailVO selectMapRescueBuildingDetail(Long buildingId) {
		MapRescueBuildingDetailVO detailVO = baseMapper.selectMapRescueBuildingDetail(buildingId);
		if (Func.isEmpty(detailVO)) {
			return null;
		}
		BuildingExt buildingExt = buildingExtService.getOne(Wrappers.<BuildingExt>lambdaQuery().eq(BuildingExt::getBuildingId, buildingId));
		List<MaintainTimeSubmitDTO> buildingMaintainTimes = buildingMaintainTimeService.selectMaintainTimes(buildingId);
		buildingExt.setBuildingMaintainTimes(buildingMaintainTimes);
		detailVO.setBuildingExt(buildingExt);
		return detailVO;
	}
}
